package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"os/signal"
	"strings"
	"syscall"

	"golang.org/x/crypto/bcrypt"
	"golang.org/x/term"
)

func main() {
	// 设置信号捕获，处理 Ctrl+C
	setupSignalHandler()

	fmt.Println("=== BCrypt 密码加密工具 ===")
	fmt.Println("按 Ctrl+C 随时退出程序")

	// 执行密码加密流程
	for {
		fmt.Println("\n1. 加密密码")
		fmt.Println("2. 验证密码")
		fmt.Println("3. 退出")
		fmt.Print("请选择操作: ")

		choice, err := readInput()
		if err != nil {
			log.Fatal("读取输入失败:", err)
		}

		switch choice {
		case "1":
			encryptPassword()
		case "2":
			verifyPassword()
		case "3":
			fmt.Println("程序已退出")
			return
		default:
			fmt.Println("无效选择，请重新输入")
		}
	}
}

// setupSignalHandler 设置信号处理器以捕获 Ctrl+C
func setupSignalHandler() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-c
		fmt.Println("\n程序已退出")
		os.Exit(0)
	}()
}

// encryptPassword 执行密码加密流程
func encryptPassword() {
	// 1. 获取密码输入
	password, err := readPassword("请输入密码: ")
	if err != nil {
		log.Printf("读取密码失败: %v", err)
		return
	}

	// 2. 确认密码
	confirm, err := readPassword("请再次输入密码确认: ")
	if err != nil {
		log.Printf("读取确认密码失败: %v", err)
		return
	}

	// 3. 验证密码是否匹配
	if password != confirm {
		fmt.Println("错误：两次输入的密码不匹配")
		return
	}

	// 4. 获取成本因子
	cost, err := readCost()
	if err != nil {
		log.Printf("读取成本因子失败: %v", err)
		return
	}

	// 5. 使用 bcrypt 加密密码
	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), cost)
	if err != nil {
		log.Printf("加密失败: %v", err)
		return
	}

	// 6. 输出结果
	fmt.Printf("\n加密成功!\n")
	fmt.Printf("成本因子: %d\n", cost)
	fmt.Printf("加密结果: %s\n", string(hashedPassword))
}

// verifyPassword 验证密码
func verifyPassword() {
	// 读取哈希值
	fmt.Print("请输入要验证的 bcrypt 哈希值: ")
	hash, err := readInput()
	if err != nil {
		log.Printf("读取哈希值失败: %v", err)
		return
	}

	// 读取密码
	password, err := readPassword("请输入要验证的密码: ")
	if err != nil {
		log.Printf("读取密码失败: %v", err)
		return
	}

	// 验证密码
	err = bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
	if err == nil {
		fmt.Println("\n验证成功: 密码匹配!")
	} else {
		fmt.Println("\n验证失败: 密码不匹配!")
	}
}

// readPassword 安全地从终端读取密码（不显示输入内容）
func readPassword(prompt string) (string, error) {
	fmt.Print(prompt)

	// 读取密码字节（不显示）
	passwordBytes, err := term.ReadPassword(int(os.Stdin.Fd()))
	if err != nil {
		return "", err
	}

	fmt.Println() // 换行
	return string(passwordBytes), nil
}

// readInput 读取普通输入
func readInput() (string, error) {
	reader := bufio.NewReader(os.Stdin)
	input, err := reader.ReadString('\n')
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(input), nil
}

// readCost 读取成本因子
func readCost() (int, error) {
	fmt.Print("请输入 bcrypt 成本因子 (默认 12, 推荐 10-14): ")
	input, err := readInput()
	if err != nil {
		return 0, err
	}

	// 处理输入
	if input == "" {
		return 12, nil // 默认值
	}

	var cost int
	_, err = fmt.Sscanf(input, "%d", &cost)
	if err != nil {
		return 0, fmt.Errorf("无效的成本因子: %s", input)
	}

	// 验证成本因子范围
	if cost < bcrypt.MinCost {
		return bcrypt.MinCost, nil
	}
	if cost > bcrypt.MaxCost {
		return bcrypt.MaxCost, nil
	}

	return cost, nil
}
